package mbsl.util;

import java.awt.image.BufferedImage;
import java.awt.image.DirectColorModel;
import java.awt.image.IndexColorModel;
import java.awt.image.WritableRaster;

import mbsl.ui.UIUtils;

import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.PaletteData;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;

/**
 * Some convenience methods to make life with SWT easier.
 * @see UIUtils
 */
public final class SWTUtils {
	/**
	 * This class attempts to simplify the tedious task of creating FormAttachments.  It doesn't really reduce the number of calls, but
	 * does reduce the amount of typing needed.  For even less typing, use the getMeta() methods. For significantly less typing,
	 * combine them with getFormData(FormMeta ... metas) 
	 * 
	 * @see SWTUtils#getMeta(Direction, Control, int)
	 * @see SWTUtils#getMeta(Direction, int, int)
	 * @see SWTUtils#ge
	 */
	public static class FormMeta {
		private Direction mDirection;
		private int mOffset;
		private Integer mNumerator = null;
		private Control mControl = null;
		private Direction mAlignment;
		
		/**
		 * Describe a FormAttachment using a numerator. 
		 */
		public FormMeta(Direction pDirection, int pNumerator, int pOffset) {
			mDirection = pDirection;
			mNumerator = pNumerator;
			mOffset = pOffset;
		}
		/**
		 * Describe a FormAttachment using another Control.
		 */
		public FormMeta(Direction pDirection, Control pControl, int pOffset) {
			mControl = pControl;
			mDirection = pDirection;
			mOffset = pOffset;
		}
		/**
		 * Describe a formAttachment using another Control, additionally specifying where to attach it.
		 */
		public FormMeta(Direction pDirection, Control pControl, int pOffset, Direction pAlignment) {
			this(pDirection, pControl, pOffset);
			
			mAlignment = pAlignment;
		}
		public Direction getDirection() {
			return mDirection;
		}
		/** Returns the FormAttachment described by this FormMeta */
		public FormAttachment getFormAttachment() {
			if (mNumerator != null)
				return new FormAttachment(mNumerator, mOffset);
			else if (mAlignment == null)
				return new FormAttachment(mControl, mOffset);
			else
				return new FormAttachment(mControl, mOffset, mAlignment.asInt());
		}

	}
	/** All four directions, and center */
	public static enum Direction {
		/** Left */
		left(SWT.LEFT), 
		/** Right */
		right(SWT.RIGHT), 
		/** Up or top */
		up(SWT.TOP), 
		/** Down or bottom */
		down(SWT.BOTTOM), 
		/** Center */
		center(SWT.CENTER);
		
		/** Returns the Direction as its SWT counterpart */
		public int asInt() {
			return mSWTValue;
		}
		
		private Direction(int pSWTValue) {
			pSWTValue = mSWTValue;
		}
		
		int mSWTValue;
	}
	/** 
	 * Returns a new FormMeta using provided specs 
	 * @see #getMeta(Direction, Control, int)
	 * @see #getMeta(Direction, Control, int, Direction)
	 */
	public static FormMeta getMeta(Direction pDirection, int pNumerator, int pOffset) {
		return new FormMeta(pDirection, pNumerator, pOffset);
	}
	/** 
	 * Returns a new FormMeta using provided specs 
	 * @see #getMeta(Direction, int, int)
	 * @see #getMeta(Direction, Control, int, Direction)
	 */
	public static FormMeta getMeta(Direction pDirection, Control pControl, int pOffset) {
		return new FormMeta(pDirection, pControl, pOffset);
	}
	/** 
	 * Returns a new FormMeta using provided specs 
	 * @see #getMeta(Direction, int, int)
	 * @see #getMeta(Direction, Control, int)
	 */
	public static FormMeta getMeta(Direction pDirection, Control pControl, int pOffset, Direction pAlignment) {
		return new FormMeta(pDirection, pControl, pOffset, pAlignment);
	}
	/** 
	 * Combines up to four FormMetas to a FormData object.
	 */
	public static FormData getFormData(FormMeta ... pFormMetas) {
		FormData formData = new FormData();
		
		for (FormMeta data : pFormMetas)
			switch (data.getDirection()) {
			case left:
				formData.left = data.getFormAttachment();
				break;
			case right:
				formData.right = data.getFormAttachment();
				break;
			case up:
				formData.top = data.getFormAttachment();
				break;
			case down:
				formData.bottom = data.getFormAttachment();
				break;
			}

		return formData;
	}
	/** 
	 * Combines up to four FormMetas to a FormData object of the provided width and height.
	 * If you only need to set one of them, set the other to SWT.DEFAULT. 
	 */
	public static FormData getFormData(int pWidth, int pHeight, FormMeta ... pFormMetas) {
		FormData formData = getFormData(pFormMetas);
		
		formData.width = pWidth;
		formData.height = pHeight;
		
		return formData;
	}
	/** Creates a simple separator label of the vertical kind. */
	public static Label getVerticalBar(Composite pComposite) {
		return new Label(pComposite, SWT.SEPARATOR | SWT.VERTICAL);
	}
	/** Creates a simple separator label of the horizontal kind. */
	public static Label getHorizontalBar(Composite pComposite) {
		return new Label(pComposite, SWT.SEPARATOR | SWT.HORIZONTAL);
	}
	/** Executes the provided Runnable on the event thread of the provided Display. */
	public static void exec(Display pDisplay, Runnable pRunnable) {
		pDisplay.asyncExec(pRunnable);
	}
	/** Executes the provided Runnable on the event thread of the provided Display.  Blocks until finished. */
	public static void blockExec(Display pDisplay, Runnable pRunnable) {
		pDisplay.syncExec(pRunnable);
	}
	/** Blocks proceedings until pShell is disposed. */
	public static void block(Shell pShell) {
		while (!pShell.isDisposed())
			try {
				if (!pShell.getDisplay().readAndDispatch())
					pShell.getDisplay().sleep();
			} catch (Exception pE) {
				pE.printStackTrace();
			}
	}
	/** Returns a bolded version of the font */
	public static Font getBold(Font pFont) {
		FontData fontData = pFont.getFontData()[0];
		
		fontData.setStyle(SWT.BOLD);
		
		return new Font(pFont.getDevice(), fontData);
	}
	/** 
	 * Returns the computed optimal size for the component. Exactly the same as
	 * <pre>control.computeSize(SWT.DEFAULT, SWT.DEFAULT)</pre>
	 */
	public static Point getSize(Control pControl) {
		return pControl.computeSize(SWT.DEFAULT, SWT.DEFAULT);
	}
	/**
	 * Converts an AWT BufferedImage to an SWT ImageData
	 * <p>
	 * This is more or less directly copied from Snippet156 ('Convert between SWT Image and AWT BufferedImage') from the Eclipse SWT pages.
	 */
	public static ImageData convertToSWT(BufferedImage bufferedImage) {
	    if (bufferedImage.getColorModel() instanceof DirectColorModel) {
			DirectColorModel colorModel = (DirectColorModel)bufferedImage.getColorModel();

			PaletteData palette = new PaletteData(colorModel.getRedMask(), colorModel.getGreenMask(), colorModel.getBlueMask());

			ImageData data = new ImageData(bufferedImage.getWidth(), bufferedImage.getHeight(), colorModel.getPixelSize(), palette);
			WritableRaster raster = bufferedImage.getRaster();
			int[] pixelArray = new int[3];

			for (int y = 0; y < data.height; y++)
				for (int x = 0; x < data.width; x++) {
					raster.getPixel(x, y, pixelArray);
					
					int pixel = palette.getPixel(new RGB(pixelArray[0], pixelArray[1], pixelArray[2]));

					data.setPixel(x, y, pixel);
				}

			return data;
		} else if (bufferedImage.getColorModel() instanceof IndexColorModel) {
			IndexColorModel colorModel = (IndexColorModel)bufferedImage.getColorModel();

			int size = colorModel.getMapSize();

			byte[] reds = new byte[size];
			byte[] greens = new byte[size];
			byte[] blues = new byte[size];

			colorModel.getReds(reds);
			colorModel.getGreens(greens);
			colorModel.getBlues(blues);
			RGB[] rgbs = new RGB[size];

			for (int i = 0; i < rgbs.length; i++)
				rgbs[i] = new RGB(reds[i] & 0xFF, greens[i] & 0xFF, blues[i] & 0xFF);

			PaletteData palette = new PaletteData(rgbs);

			ImageData data = new ImageData(bufferedImage.getWidth(), bufferedImage.getHeight(), colorModel.getPixelSize(), palette);

			data.transparentPixel = colorModel.getTransparentPixel();
			WritableRaster raster = bufferedImage.getRaster();

			int[] pixelArray = new int[1];

			for (int y = 0; y < data.height; y++)
				for (int x = 0; x < data.width; x++) {
					raster.getPixel(x, y, pixelArray);
					data.setPixel(x, y, pixelArray[0]);
				}

			return data;
		}

	    return null;
	}
	
	private SWTUtils() {}
}
